package x.ovo.wechat.bot.core.http.request;


import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;
import lombok.experimental.Accessors;
import x.ovo.wechat.bot.core.Constant;
import x.ovo.wechat.bot.core.Context;
import x.ovo.wechat.bot.core.http.session.Session;

import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 * API 请求的基类，封装了请求的参数和响应的结果。
 * 该类为抽象类，用于定义通用的API请求行为和属性。
 *
 * @author ovo, created by 2024/07/06
 */
@Getter
@Setter
@Accessors(chain = true)
@ToString
//@NoArgsConstructor
public abstract class ApiRequest<T> {
    /** 请求方法类型：GET */
    protected static final String GET = "GET";
    /** 请求方法类型：POST */
    protected static final String POST = "POST";

    /** 当前会话对象 */
    protected Session session;
    /** 基础请求对象 */
    protected BaseRequest baseRequest;
    /** 请求URL */
    protected String url;
    /** 保存文件的名称 */
    protected String filename;

    /** 请求超时时间（单位：毫秒），0表示使用默认超时时间 */
    protected int timeout = 0;
    /** 请求方法，默认为GET */
    protected String method = GET;
    /** 是否以JSON格式发送请求体 */
    protected boolean json = false;
    /** 是否使用multipart/form-data格式发送请求体 */
    protected boolean multipart = false;
    /** 是否禁止自动重定向 */
    protected boolean noRedirect = false;

    /** 请求头信息 */
    protected final Map<String, String> headers = new HashMap<>();
    /** 请求参数 */
    protected final Map<String, Object> parameters = new HashMap<>();
    /** 请求内容类型，默认为表单提交格式 */
    protected String contentType = "application/x-www-form-urlencoded; charset=UTF-8";

    protected Class<?> handlerType = String.class;

    private final Map<Class<?>, Function<?, T>> handlers = new HashMap<>() {{
        put(String.class, stringHandler());
        put(byte[].class, byteArrHandler());
        put(InputStream.class, streamHandler());
    }};

    /**
     * 构建实际的API请求对象
     *
     * @return API请求对象
     */
    public abstract ApiRequest<T> build();

    /**
     * 处理响应结果的函数
     *
     * @return 响应处理函数
     */
    protected Function<String, T> stringHandler() {
        return s -> null;
    }

    protected Function<byte[], T> byteArrHandler() {
        return null;
    }

    protected Function<InputStream, T> streamHandler() {
        return null;
    }

    /**
     * 构造函数，初始化请求的默认设置
     *
     * @param session 当前会话
     */
    public ApiRequest(Session session) {
        if (Objects.nonNull(session)) {
            this.session = session;
            this.baseRequest = session.getBaseRequest();
        }
        this.setHeaders();
    }

    public ApiRequest() {
        this(Context.INSTANCE.getSession());
    }

    /**
     * 设置默认的请求头信息
     */
    private void setHeaders() {
        // 设置一系列默认的请求头，包括接受类型、用户代理、连接方式、语言、内容类型等
        this.headers.put("Accept", "*/*");
        this.headers.put("User-Agent", Constant.USER_AGENT);
//        this.headers.put("Connection", "close");
        this.headers.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
        this.headers.put("Content-Type", this.contentType);
        this.headers.put("Client-Version", "2.0.0");
        this.headers.put("extspam", Constant.EXTSPAM);
        this.headers.put("Dnt", "1");
        // 尝试从会话URL中获取主机和协议信息，设置Host和Referer头
        try {
            URL url1 = new URL(session.getUrl());
            this.headers.put("Host", url1.getHost());
            this.headers.put("Origin", url1.getProtocol() + "://" + url1.getHost());
            this.headers.put("Referer", url1.getProtocol() + "://" + url1.getHost() + "/?target=t");
        } catch (Exception ignore) {
            // 异常忽略，可能的URL解析错误
        }
    }

    /**
     * 添加请求头信息
     *
     * @param key   头信息的键
     * @param value 头信息的值
     * @return 当前请求对象，用于链式调用
     */
    public ApiRequest<T> addHeader(String key, String value) {
        this.headers.put(key, value);
        return this;
    }

    /**
     * 添加请求参数
     *
     * @param key   参数的键
     * @param value 参数的值
     * @return 当前请求对象，用于链式调用
     */
    public ApiRequest<T> addParameter(String key, Object value) {
        this.parameters.put(key, value);
        return this;
    }

    /**
     * 执行请求，并返回处理后的响应结果
     *
     * @return 响应结果对象
     */
    @SneakyThrows
    public T execute() {
        this.build();
        // 如果请求体以JSON格式发送，设置相应的Content-Type头
        if (this.json) {
            this.contentType = "application/json; charset=UTF-8";
            this.addHeader("Content-Type", this.contentType);
        }
        // 执行请求，并通过响应处理函数处理响应结果
        return Context.INSTANCE.getHttpEngine().execute(this, this.handlers.get(this.handlerType), this.handlerType);
    }

}
